using System;
using System.Collections.Generic;
using System.Text;
using System.IO;


namespace Skeletons
{
    public class Exporter
    {
        private Form1 m_refParent = null;
        private Animation.Animator m_rAnimator = null;
        private double m_dScale = 100.0;
        private List<String> m_strFile;
        private StreamWriter m_swrFile;

        public Exporter(Form1 p_refParent) : base()
        {
            m_refParent = p_refParent;

            //m_strFile = new List<string>();
            m_swrFile = null; 
            System.Console.WriteLine("Exporter Initialized!");
        }

        private void makeHeader()
        {
            m_swrFile.WriteLine("<?xml version=\"1.0\"?>");
            m_swrFile.WriteLine("<canvas version=\"0.6\" width=\"640\" height=\"360\" xres=\"2834.645752\" yres=\"2834.645752\" view-box=\"-4.000000 2.250000 4.000000 -2.250000\" antialias=\"1\" fps=\"24.000\" begin-time=\"0f\" end-time=\"5s\" bgcolor=\"0.500000 0.500000 0.500000 1.000000\">");
            m_swrFile.WriteLine("  <name>Animazione ArtIA1</name>");
        }

        private void makeFooter()
        {
            m_swrFile.WriteLine("</canvas>");
        }

        private void startLayer(String p_strType, String p_strName, Rigging.RiggingEntity p_retEntity)
        {
            m_swrFile.WriteLine("  <layer type=\"" + p_strType + "\" active=\"true\" version=\"0.1\" desc=\"" + p_strName + "\">");

            m_swrFile.WriteLine("    <param name=\"z_depth\">");
            m_swrFile.WriteLine("      <real value=\"0.0000000000\"/>");
            m_swrFile.WriteLine("    </param>");

            m_swrFile.WriteLine("    <param name=\"amount\">");
            m_swrFile.WriteLine("      <real value=\"1.0000000000\"/>");
            m_swrFile.WriteLine("    </param>");
  
            m_swrFile.WriteLine("    <param name=\"blend_method\">");
            m_swrFile.WriteLine("      <integer value=\"0\"/>");
            m_swrFile.WriteLine("    </param>");

            m_swrFile.WriteLine("    <param name=\"color\">");
            m_swrFile.WriteLine("      <color>");
            m_swrFile.WriteLine("        <r>" + ((float)p_retEntity.PenColor.Color.R / 255F).ToString().Replace(",", ".") + "</r>");
            m_swrFile.WriteLine("        <g>" + ((float)p_retEntity.PenColor.Color.G / 255F).ToString().Replace(",", ".") + "</g>");
            m_swrFile.WriteLine("        <b>" + ((float)p_retEntity.PenColor.Color.B / 255F).ToString().Replace(",", ".") + "</b>");
            m_swrFile.WriteLine("        <a>" + ((float)p_retEntity.PenColor.Color.A / 255F).ToString().Replace(",", ".") + "</a>");
            m_swrFile.WriteLine("      </color>");
            m_swrFile.WriteLine("    </param>");

            m_swrFile.WriteLine("    <param name=\"origin\">");
            m_swrFile.WriteLine("      <vector>");
            m_swrFile.WriteLine("        <x>0.0000000000</x>");
            m_swrFile.WriteLine("        <y>0.0000000000</y>");
            m_swrFile.WriteLine("      </vector>");
            m_swrFile.WriteLine("    </param>");
  
            m_swrFile.WriteLine("    <param name=\"invert\">");
            m_swrFile.WriteLine("      <bool value=\"false\"/>");
            m_swrFile.WriteLine("    </param>");

            m_swrFile.WriteLine("    <param name=\"antialias\">");
            m_swrFile.WriteLine("      <bool value=\"true\"/>");
            m_swrFile.WriteLine("    </param>");
  
            m_swrFile.WriteLine("    <param name=\"feather\">");
            m_swrFile.WriteLine("      <real value=\"0.0000000000\"/>");
            m_swrFile.WriteLine("    </param>");
  
            m_swrFile.WriteLine("    <param name=\"blurtype\">");
            m_swrFile.WriteLine("      <integer value=\"1\"/>");
            m_swrFile.WriteLine("    </param>");
 
            m_swrFile.WriteLine("    <param name=\"winding_style\">");
            m_swrFile.WriteLine("      <integer value=\"0\"/>");
            m_swrFile.WriteLine("    </param>");
        }

        private void startFreeHand()
        {
            m_swrFile.WriteLine("    <param name=\"vector_list\">");
            m_swrFile.WriteLine("      <dynamic_list type=\"vector\">");
        }

        private void stopFreeHand()
        {
            m_swrFile.WriteLine("      </dynamic_list>");
            m_swrFile.WriteLine("    </param>");
        }

        private void startBLine()
        {
            m_swrFile.WriteLine("    <param name=\"bline\">");
            m_swrFile.WriteLine("      <bline type=\"bline_point\" loop=\"false\" guid=\"\">");
        }

        private void stopBLine()
        {
            m_swrFile.WriteLine("      </bline>");
            m_swrFile.WriteLine("    </param>");
        }

        private void stopLayer()
        {
            m_swrFile.WriteLine("  </layer>");
        }

        private void startAnimatedEntry()
        {
            m_swrFile.WriteLine("        <entry>");
            m_swrFile.WriteLine("          <animated type=\"vector\">");
        }

        private void stopAnimatedEntry()
        {
            m_swrFile.WriteLine("          </animated>");
            m_swrFile.WriteLine("        </entry>");
        }

        private void addBLineStaticEntry(double p_dX, double p_dY, double p_dT1Radius, double p_dT1Theta, double p_dT2Radius, double p_dT2Theta)
        {
            m_swrFile.WriteLine("          <entry>");
            m_swrFile.WriteLine("            <composite type=\"bline_point\">");
            m_swrFile.WriteLine("              <point>");
            m_swrFile.WriteLine("                <vector>");
            m_swrFile.WriteLine("                  <x>" + p_dX.ToString().Replace(',', '.') + "</x>");
            m_swrFile.WriteLine("                  <y>" + p_dY.ToString().Replace(',', '.') + "</y>");
            m_swrFile.WriteLine("                </vector>");
            m_swrFile.WriteLine("              </point>");
            m_swrFile.WriteLine("              <width>");
            m_swrFile.WriteLine("                <real value=\"1.0000000000\"/>");
            m_swrFile.WriteLine("              </width>");
            m_swrFile.WriteLine("              <origin>");
            m_swrFile.WriteLine("                <real value=\"0.5000000000\"/>");
            m_swrFile.WriteLine("              </origin>");
            m_swrFile.WriteLine("              <split>");
            m_swrFile.WriteLine("                <bool value=\"false\"/>");
            m_swrFile.WriteLine("              </split>");
            m_swrFile.WriteLine("              <t1>");
            m_swrFile.WriteLine("                <radial_composite type=\"vector\">");
            m_swrFile.WriteLine("                  <radius>");
            m_swrFile.WriteLine("                    <real value=\"" + p_dT1Radius.ToString().Replace(',', '.') + "\"/>");
            m_swrFile.WriteLine("                  </radius>");
            m_swrFile.WriteLine("                  <theta>");
            m_swrFile.WriteLine("                    <angle value=\"" + p_dT1Theta.ToString().Replace(',', '.') + "\"/>");
            m_swrFile.WriteLine("                  </theta>");
            m_swrFile.WriteLine("                </radial_composite>");
            m_swrFile.WriteLine("              </t1>");
            m_swrFile.WriteLine("              <t2>");
            m_swrFile.WriteLine("                <radial_composite type=\"vector\">");
            m_swrFile.WriteLine("                  <radius>");
            m_swrFile.WriteLine("                    <real value=\"" + p_dT2Radius.ToString().Replace(',', '.') + "\"/>");
            m_swrFile.WriteLine("                  </radius>");
            m_swrFile.WriteLine("                  <theta>");
            m_swrFile.WriteLine("                    <angle value=\"" + p_dT2Theta.ToString().Replace(',', '.') + "\"/>");
            m_swrFile.WriteLine("                  </theta>");
            m_swrFile.WriteLine("                </radial_composite>");
            m_swrFile.WriteLine("              </t2>");
            m_swrFile.WriteLine("            </composite>");
            m_swrFile.WriteLine("          </entry>");
        }

        private void addEntry(double p_dX, double p_dY)
        {
            m_swrFile.WriteLine("        <entry>");
            m_swrFile.WriteLine("          <vector>");
            m_swrFile.WriteLine("            <x>" + p_dX.ToString("").Replace(',', '.') + "</x>");
            m_swrFile.WriteLine("            <y>" + p_dY.ToString("").Replace(',', '.') + "</y>");
            m_swrFile.WriteLine("          </vector>");
            m_swrFile.WriteLine("        </entry>");
        }

        private void addWaypoint(double p_dTime, double p_dX, double p_dY)
        {
            m_swrFile.WriteLine("            <waypoint time=\"" + p_dTime.ToString().Replace(',', '.') + "s\" before=\"auto\" after=\"auto\">");
            m_swrFile.WriteLine("              <vector guid=\"\">");
            m_swrFile.WriteLine("                <x>" + p_dX.ToString("").Replace(',', '.') + "</x>");
            m_swrFile.WriteLine("                <y>" + p_dY.ToString("").Replace(',', '.') + "</y>");
            m_swrFile.WriteLine("              </vector>");
            m_swrFile.WriteLine("            </waypoint>");
        }

        private void doExportBLineStatic(double[] p_arrPoints, double[] p_arrCtrl)
        {
            addBLineStaticEntry(p_arrPoints[0], p_arrPoints[1], p_arrCtrl[0], p_arrCtrl[1], p_arrCtrl[0], p_arrCtrl[1]);
            addBLineStaticEntry(p_arrPoints[2], p_arrPoints[3], p_arrCtrl[2], p_arrCtrl[3], p_arrCtrl[2], p_arrCtrl[3]);
        }

        private void doExportStatic(System.Drawing.Point[] p_refPoints)
        {
            for (int k = 0; k < p_refPoints.Length; k++)
            {
                addEntry(p_refPoints[k].X / m_dScale, -p_refPoints[k].Y / m_dScale);
            }
        }

		private void exportBLine(
				System.Drawing.Point p1,
				System.Drawing.Point c1,
				System.Drawing.Point c2,
				System.Drawing.Point p2,
				Bone bone,
				Rigging.RiggingEntity entity,
				int k
			)
		{
			double p1X = p1.X; double p1Y = p1.Y;
			double c1X = c1.X; double c1Y = c1.Y;
			double c2X = c2.X; double c2Y = c2.Y;
			double p2X = p2.X; double p2Y = p2.Y;

			p1Y = -1 * p1Y / m_dScale; p1X = p1X / m_dScale;
			p2Y = -1 * p2Y / m_dScale; p2X = p2X / m_dScale;
			c1Y = -1 * c1Y / m_dScale; c1X = c1X / m_dScale;
			c2Y = -1 * c2Y / m_dScale; c2X = c2X / m_dScale;

			double l1X = c1X - p1X; double l1Y = c1Y - p1Y;
			double l2X = c2X - p2X; double l2Y = c2Y - p2Y;
			double l21X = p2X - p1X; double l21Y = p2Y - p1Y;
			double l12X = p1X - p2X; double l12Y = p1Y - p2Y;

			/*
			System.Drawing.Point l1 = new System.Drawing.Point();
				l1.X  = c1X - p1X;
				l1.Y  = c1Y - p1Y;
			System.Drawing.Point l2 = new System.Drawing.Point();
				l2.X  = c2X - p2X;
				l2.Y  = c2Y - p2Y;
			System.Drawing.Point l21 = new System.Drawing.Point();
				l21.X = p2X - p1X;
				l21.Y = p2Y - p1Y;
			System.Drawing.Point l12 = new System.Drawing.Point();
				l12.X = p1X - p2X;
				l12.Y = p1Y - p2Y;
			*/

			double n1 = Math.Sqrt(l1X * l1X + l1Y * l1Y);
			double n2 = Math.Sqrt(l2X * l2X + l2Y * l2Y);
			double n = Math.Sqrt(l12X * l12X + l12Y * l12Y);

			/*
			System.Drawing.Point line1 = new System.Drawing.Point(c1.X - p1.X, c1.Y - p1.Y);
			System.Drawing.Point line2 = new System.Drawing.Point(c2.X - p2.X, c2.Y - p2.Y);
			System.Drawing.Point line = new System.Drawing.Point(p2.X - p1.X, p2.Y - p1.Y);
			System.Drawing.Point norm = new System.Drawing.Point(1, 0);
			*/

			//double roff = Math.Acos(( line.X * norm.X +    line.Y * norm.Y) / (Math.Sqrt( line.X *  line.X +  line.Y *  line.Y))) * (180.0 / Math.PI);

			double r1 = (l1Y < 0 ? -1 : 1) * Math.Acos((l1X) / (Math.Sqrt(l1X * l1X + l1Y * l1Y))) * (180.0 / Math.PI);
			double r2 = (l2Y < 0 ? -1 : 1) * Math.Acos((l2X) / (Math.Sqrt(l2X * l2X + l2Y * l2Y))) * (180.0 / Math.PI) + 180;


			/*
			double r1 =       Math.Acos((l1.X * l21.X + l1.Y * l21.Y) / (n1 * n)) * (180.0 / Math.PI) + roff;
			double r2 = 360 - Math.Acos((l2.X * l12.X + l2.Y * l12.Y) / (n2 * n)) * (180.0 / Math.PI) + roff;
			*/

			double[] lstPoints = new double[4];
			lstPoints[0] = p1X;
			lstPoints[1] = p1Y;
			lstPoints[2] = p2X;
			lstPoints[3] = p2Y;

			double[] lstCtrl = new double[4];
			lstCtrl[0] = n1;
			lstCtrl[1] = r1;
			lstCtrl[2] = n2;
			lstCtrl[3] = r2;

			// ##### Esportazione
			startLayer("region", bone.Id + "_" + k.ToString().PadLeft(3, '0'), entity);
				startBLine();
					doExportBLineStatic(lstPoints, lstCtrl);
				stopBLine();
			stopLayer();
			
		}

        private void doExportAnimated(int p_iFrameRate, ref List<int> p_refKFrames, ref List<System.Drawing.Point[]> p_refPointsList)
        {
            Console.WriteLine("p_refPointsList: " + p_refPointsList.Count);

            for(int p = 0; p < p_refPointsList[0].Length; p++)
            {
                startAnimatedEntry();

                for (int i = 0; i < p_refKFrames.Count; i++)
                {
                    addWaypoint((double)p_refKFrames[i] / (double)p_iFrameRate, (double)p_refPointsList[i][p].X / m_dScale, - (double)p_refPointsList[i][p].Y / m_dScale);
                }

                stopAnimatedEntry();
            }
        }

        public bool doExport(string filename, ref Animation.Animator p_rAnimator, ref List<Skeleton> p_rSkels, double p_dScale)
        {
            m_rAnimator = p_rAnimator;
            m_dScale = p_dScale;
            // empty the string list from previous doExport() call

            //m_strFile.Clear();
            m_swrFile = new StreamWriter(filename);

            makeHeader();

            foreach (Skeleton currSkel in p_rSkels)
            {
                Bone[] bones = currSkel.Bones.ToArray();

                foreach (Bone currBone in bones)
                {
                    System.Console.WriteLine("Examining bone " + currBone.Id);

                    //// Manuel, just for debug cane9
                    //if (currBone.Id == "Bone015")
                    //    System.Diagnostics.Debugger.Break();

                    int k = 0;
                    if (currBone.Skin != null)
                        foreach (Rigging.RiggingEntity currEntity in currBone.Skin.Entities)
                        {
                            List<int> lstKFrames = m_rAnimator.Timeline.GetEntityMovements(currEntity);

                            if (lstKFrames.Count == 0)
                            {
                                // Static Export
                                p_rAnimator.RenderFrame(0);

                                System.Drawing.Drawing2D.Matrix matrix = new System.Drawing.Drawing2D.Matrix();

                                System.Drawing.Point[] arrPoints = currEntity.Points.ToArray();
                                System.Drawing.Point off = m_refParent.getCanvasOffset();

                                matrix.Translate(currBone.p1.X - off.X, currBone.p1.Y - off.Y);
                                matrix.Rotate((float)currBone.Angle);
                                matrix.TransformPoints(arrPoints);

                                if (currEntity.GetType().ToString() == "Rigging.FreeHand")
                                {
                                    // ##### Preprocessing dei dati da esportare

                                    // ##### Esportazione
                                    startLayer("polygon", currBone.Id + "_" + k.ToString().PadLeft(3, '0'), currEntity);
                                        startFreeHand();
                                            doExportStatic(arrPoints);
                                        stopFreeHand();
                                    stopLayer();
                                }

								if (currEntity.GetType().ToString() == "Rigging.SPLine")
								{
									int count = currEntity.Points.Count;
									int offset = 0;
									while(offset < (count - 3))
									{
										exportBLine(arrPoints[offset], arrPoints[offset + 1], arrPoints[offset + 2], arrPoints[offset + 3], currBone,currEntity,k);
										offset += 3;										
									}
								}

                                if (currEntity.GetType().ToString() == "Rigging.BLine")
                                {
									exportBLine(arrPoints[0],arrPoints[1],arrPoints[2],arrPoints[3], currBone,currEntity,k);

                                }

                            }
                            else
                            {
                                // Animated Export
                                List<System.Drawing.Point[]> lstArrays = new List<System.Drawing.Point[]>(lstKFrames.Count);

                                for (int i = 0; i < lstKFrames.Count; i++)
                                {
                                    p_rAnimator.RenderFrame(lstKFrames[i]);

                                    System.Drawing.Drawing2D.Matrix matrix = new System.Drawing.Drawing2D.Matrix();
                                    System.Drawing.Point[] arrPoints = currEntity.Points.ToArray();

                                    System.Drawing.Point off = m_refParent.getCanvasOffset();

                                    matrix.Translate(currBone.p1.X - off.X, currBone.p1.Y - off.Y);
                                    matrix.Rotate((float)currBone.Angle);
                                    matrix.TransformPoints(arrPoints);

                                    lstArrays.Add(arrPoints);
                                }

                                if (currEntity.GetType().ToString() == "Rigging.FreeHand")
                                {
                                    startLayer("polygon", currBone.Id + "_" + k.ToString().PadLeft(3, '0'), currEntity);
                                        startFreeHand();
                                            doExportAnimated(p_rAnimator.Timeline.FrameRate, ref lstKFrames, ref lstArrays);
                                        stopFreeHand();
                                    stopLayer();
                                    
                                }

                                if (currEntity.GetType().ToString() == "Rigging.BLine")
                                {
                                    startLayer("region", currBone.Id + "_" + k.ToString().PadLeft(3, '0'), currEntity);

                                    stopLayer();
                                }

								if (currEntity.GetType().ToString() == "Rigging.SPLine")
								{
									startLayer("region", currBone.Id + "_" + k.ToString().PadLeft(3, '0'), currEntity);

									stopLayer();
								}

                            }

                            Console.WriteLine("p_refEntity: " + lstKFrames.Count);

                            k++;
                        }
                }
            }

            makeFooter();

            //saveXML(filename);
            m_swrFile.Close();

            return true;
        }

        private bool saveXML(string filename)
        {
            //FileStream f = new FileStream("output.sif", FileMode.Create);
            StreamWriter s = new StreamWriter(filename, false);

            foreach( String str in m_strFile)
                s.WriteLine(str);

            s.Close();
            //f.Close();

            return true;
        }

    }
}
